package top.jfunc.common.db.data;


import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntConsumer;

import javax.sql.DataSource;


/**
 * SQL 数据读取器
 *
 * @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
 * @since 1.0 2022/11/25 11:21
 */
public abstract class DataExtractor {
  protected final DataSource dataSource;

  // @Nullable
  protected final Consumer<Throwable> exceptionConsumer;

  // @Nullable
  protected final PreparedStatementSetter statementSetter;

  // @Nullable
  protected final IntConsumer successCallback;

  protected DataExtractor(DataSource dataSource,
      Consumer<Throwable> exceptionConsumer,
      PreparedStatementSetter statementSetter, IntConsumer successCallback) {
    this.dataSource = dataSource;
    this.exceptionConsumer = exceptionConsumer;
    this.statementSetter = statementSetter;
    this.successCallback = successCallback;
  }

  /**
   * @param handler 数据行回调处理
   * @return 处理的行数
   */
  public abstract int processRow(RowCallbackHandler handler);

  protected void configStatement(PreparedStatement statement) throws SQLException {
    if (statementSetter != null) {
      statementSetter.setValues(statement);
    }
  }

  protected PreparedStatement prepareStatement(Connection con, String querySQL) throws SQLException {
    return con.prepareStatement(querySQL, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
  }

  public DataSource getDataSource() {
    return dataSource;
  }

  // Static builder methods

  public static SimpleBuilder simple() {
    return new SimpleDataExtractor.Builder();
  }

  public static SimpleBuilder fetch(String querySQL, DataSource dataSource) {
    return SimpleDataExtractor.fetch(querySQL, dataSource);
  }

  public static PageableBuilder fetch(int pageSize, DataSource dataSource) {
    return pageReading(pageSize, dataSource);
  }

  public static PageableBuilder pageReading() {
    return PageableSQLDataExtractor.forBuilder();
  }

  public static PageableBuilder pageReading(DataSource dataSource) {
    return pageReading()
        .dataSource(dataSource);
  }

  public static PageableBuilder pageReading(int pageSize, DataSource dataSource) {
    return pageReading()
        .pageSize(pageSize)
        .dataSource(dataSource);
  }

  public static PageableBuilder pageReading(int pageSize) {
    return pageReading()
        .pageSize(pageSize);
  }

  public interface Builder<B extends Builder<B>> {

    /**
     * 数据源
     */
    B dataSource(DataSource dataSource);

    /**
     * 异常处理
     */
    B errorHandling(Consumer<Throwable> consumer);

    /**
     * 设置 PreparedStatement
     *
     * @return builder
     */
    B config(PreparedStatementSetter statementPostProcessor);

    /**
     * 配置异常处理为抛出
     */
    default B sneakyThrow() {
      return errorHandling(ExceptionUtils::sneakyThrow);
    }

    /**
     * 成功执行任务后回调
     *
     * @param successCallback 回调
     * @return builder
     */
    B onSuccess(IntConsumer successCallback);

    DataExtractor build();

    default void subscribe(RowCallbackHandler handler) {
      DataExtractor dataExtractor = build();
      dataExtractor.processRow(handler);
    }

    default <E> List<E> collect(ResultSetExtractor<E> extractor) {
      return collect(extractor, 10_000);
    }

    default <E> List<E> collect(ResultSetExtractor<E> extractor, int initialCapacity) {
      ArrayList<E> res = new ArrayList<>(initialCapacity);
      subscribe((rs, rowNumber) -> {
        E element = extractor.extractData(rs);
        res.add(element);
      });
      return res;
    }

    default void subscribe(DataBatchUpdater.ExtractDataBuilder handler) {
			handler.from(this);
    }

    default void subscribe(Consumer<DataBatchUpdater.ExtractDataBuilder> consumer) {
        DataBatchUpdater.ExtractDataBuilder directed = DataBatchUpdater.directExtract();
        consumer.accept(directed);
        subscribe(directed);
    }

  }

  public interface PageableBuilder extends Builder<PageableBuilder> {

    PageableBuilder pageSize(int pageSize);

    /**
     * 数据行应该包括 orderBy 的 sortKey
     */
    PageableBuilder select(String selectClause);

    PageableBuilder from(String fromClause);

    PageableBuilder where(String whereClause);

    PageableBuilder groupBy(String groupClause);

    /**
     * 排序
     * <p>
     * 该字段应该出现在数据行中
     */
    PageableBuilder orderBy(Map<String, Order> sortKeys);

    /**
     * 排序
     * <p>
     * 该字段应该出现在数据行中
     *
     * @param sortKey 排序的字段
     * @param order 排序的方向
     */
    PageableBuilder orderBy(String sortKey, Order order);

    /**
     * 排序，默认升序
     * <p>
     * 该字段应该出现在数据行中
     * @param sortKey 排序的字段
     */
    PageableBuilder orderBy(String sortKey);

    /**
     * 回调会在每页的数据读取完毕后发生
     *
     * @param current 当前页的回调
     */
    PageableBuilder onPageFinished(IntConsumer current);

  }

  public interface SimpleBuilder extends Builder<SimpleBuilder> {

    SimpleBuilder querySQL(String querySQL);

    /**
     * 可以设置多个修改器
     */
    SimpleBuilder modifySQL(Function<String, String> sqlModifier);

  }

}
